﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

using OpGen.Compiler.Parser;

namespace OpGen.Compiler
{
    /// <summary>
    /// Stellt eine OPG Projekt dar
    /// </summary>
    public class ProjectElement : Element
    {
        /// <summary>
        /// Erstellt eine neue Instanz der ProjectElement-Klasse
        /// </summary>
        public ProjectElement() : base(null, ElementTypes.Project)
        {
            Projectname = "N/A";
            IncludeFiles = new IncludeFileCollection();
        }

        /// <summary>
        /// Lifert die Include Dateien die zum Projekt gehören
        /// </summary>
        public IncludeFileCollection IncludeFiles { get; private set; }

        /// <summary>
        /// Liefert die Quellcode datei
        /// </summary>
        public FileInfo SourceFile { get; set; }

        /// <summary>
        /// Liefert den Projektnamen
        /// </summary>
        public string Projectname { get; set; }

        /// <summary>
        /// Ermittelt den Referenztyp
        /// </summary>
        /// <param name="ReferenceName"></param>
        /// <returns></returns>
        public Element FindReferenceType(string ReferenceName)
        {
            var stack = new Stack<Element>();

            foreach (var item in IncludeFiles)
                stack.Push(item.Project);

            stack.Push(this);

            while (stack.Count > 0)
            {
                var currentElement = stack.Pop();

                foreach (var childElement in currentElement.ChildElements)
                {
                    stack.Push(childElement);
                }
                if (currentElement.Elementname.ToLower() == ReferenceName.ToLower() || currentElement.Fullname.ToLower() == ReferenceName.ToLower())
                    return currentElement;
            }

            return null;
        }

        /// <summary>
        /// Ermittelt den nächsten verfügbaren MessageCode
        /// </summary>
        /// <returns></returns>
        public int GetNextMessageCode()
        {
            var result = from Namespace in ChildElements
                         from Element in Namespace.ChildElements
                         where Element is MessageElement
                         select ((MessageElement)Element).Code;

            if (result.Count() == 0)
                return 0;
            else
                return result.Max() + 1;
        }

        /// <summary>
        /// Liefert ein MessageElement anhand des Message Code
        /// </summary>
        /// <param name="Code"></param>
        /// <returns></returns>
        public MessageElement GetMessageByCode(int Code)
        {
            var result = from Namespace in ChildElements
                      from Element in Namespace.ChildElements
                      where Element is MessageElement && ((MessageElement)Element).Code == Code
                      select Element;

            return (MessageElement)result.FirstOrDefault(null);
        }

        /// <summary>
        /// Fügt eine Include Datei hinzu
        /// </summary>
        /// <param name="File"></param>
        /// <param name="Parser"></param>
        internal void AddIncludeFile(FileInfo File, IOpGenParser Parser)
        {
            Logger.Normal("Add Include : {0}", File.Name);

            var newInclude = new IncludeFile(File, Parser);
            IncludeFiles.Add(newInclude);
        }

        /// <summary>
        /// Gibt an das der OpCode Enum erstellt werden soll
        /// </summary>
        public bool BuildCodeEnum { get; set; }

        /// <summary>
        /// Fügt den Ausgabedaten die OpGen Erweiterung hinzu
        /// </summary>
        public bool UseExtension { get; set; }

        /// <summary>
        /// Gibt den Namespace des OpCode Enum an
        /// </summary>
        public NamespaceElement CodeEnumNamespace { get; set; }

        /// <summary>
        /// Gibt an ob der Smart Modus aktiviert werden soll
        /// 
        /// Bei CS Ausgabedateien wird auf zusätzliche Tabulatoren und Zeilenumbrüche verzichtet
        /// </summary>
        public bool CSSmartMode { get; set; }
    }
}
